package de.develop.server;

import de.develop.commands.Commands;
import de.develop.enums.DebugTypes;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.ArrayList;

/**
 * A ChatServer for communication between the connected clients.
 *
 * @author Nuno Freitas (nunofreitas@gmail.com)
 */
public class GameServer extends Thread
{

    protected ServerSocket socketServer;
    protected int port;
    protected boolean listening;
    protected ArrayList<Player> waitingClientConnections;
    protected ArrayList<Player> readyClientConnections;
    protected int playerIndex;

    /**
     * Creates a new instance of ChatServer.
     *
     * @param serverPort the port to be used by the server
     */
    public GameServer( int serverPort )
    {
        this.port = serverPort;
        this.waitingClientConnections = new ArrayList<Player>();
        this.readyClientConnections = new ArrayList<Player>();
        this.listening = false;
        playerIndex = 0;
    }

    /**
     * Gets the server's port.
     *
     * @return the port of the server
     */
    public int getPort()
    {
        return this.port;
    }

    /**
     * Gets the number of clients.
     *
     * @return the number of clients
     */
    public int getClientCount()
    {
        return this.waitingClientConnections.size() + this.readyClientConnections.size();
    }

    /**
     * Roots a debug message to the main application.
     * 
     * @param msg the debug message to be sent to the main application
     */
    protected void debug( String msg )
    {
        Main.debug( DebugTypes.MAIN, "GameServer (" + this.port + ")", msg );
    }

    /**
     * Listens for client connections and handles them to ChatServerConnections.
     */
    @Override
    public void run()
    {
        try
        {
            this.socketServer = new ServerSocket( this.port );

            this.listening = true;
            debug( "listening" );

            while ( listening )
            {

                Socket socket = this.socketServer.accept();
                debug( "client connection from " + socket.getRemoteSocketAddress() );
                socket.setTcpNoDelay( true );

                Player socketConnection = new Player( socket, this );
                socketConnection.start();
                this.waitingClientConnections.add( socketConnection );
              
            }

        }
        catch ( Exception e )
        {
            debug( e.getMessage() );
        }
    }

    public void writeToAll( String msg )
    {
        try
        {
            for ( int i = 0; i < this.waitingClientConnections.size(); i++ )
            {
                Player client = this.waitingClientConnections.get( i );
                client.write( msg );
            }
            debug( "broadcast message '" + msg + "' was sent" );
        }
        catch ( Exception e )
        {
            debug( "Exception (writeToAll): " + e.getMessage() );
        }
    }

    public void writeToClient( int playerId, String msg )
    {
        Player client = this.waitingClientConnections.get( playerId );
        client.write( msg );
        debug( msg );
    }

    /**
     * Removes a client from the server (it's expected that the client closes its own connection).
     *
     * @param remoteAddress the remote address of the client's socket connection
     * @return true if the client was successfully removed
     */
    public synchronized boolean remove( SocketAddress remoteAddress )
    {
        debug( "entering gameServer.remove()..." );
        try
        {
            for ( int i = 0; i < this.waitingClientConnections.size(); i++ )
            {
                Player client = this.waitingClientConnections.get( i );

                if ( client.getRemoteAddress().equals( remoteAddress ) )
                {
                    this.waitingClientConnections.remove( i );
                    debug( "client " + remoteAddress + " was removed" );
                    writeToAll( remoteAddress + " has disconnected." );

                    return true;
                }
            }
        }
        catch ( Exception e )
        {
            debug( "Exception (remove): " + e.getMessage() );
        }

        return false;
    }

    public synchronized ArrayList<Player> getReadyClientConnections()
    {
        return readyClientConnections;
    }

    public synchronized ArrayList<Player> getWaitingClientConnections()
    {
        return waitingClientConnections;
    }

    /**
     * Closes the server's socket.
     */
    protected void finalize()
    {
        try
        {
            this.socketServer.close();
            this.listening = false;
            debug( "stopped" );
        }
        catch ( IOException e )
        {
            debug( "Exception (finalize): " + e.getMessage() );
        }
    }
}
